#!/usr/bin/perl
use Cwd;
use File::Copy;

# version $Id: listPath.cgi 24 2009-12-08 17:56:17Z olivier.courtade@gmail.com $

# globals
my $accountneeded='admin';
my $shares_conf="/usr/syno/synoman/phpsrc/SynoNeuf/data/shares.conf";
my $httpd_synoneuf_conf="/var/packages/SynoNeuf/target/conf/httpd-synoneuf.conf-user";

my $lang;
my $langfile;
my %translation;
# load translations
my $path=$0;
$path=~ s#/[^/]*$##;




my %postFields = ();
my %queryString = ();
my $fullpath=$0;
$fullpath=~s/[^\/]+$//;

$cmd=$ENV{'QUERY_STRING'};

unless ( is_admin_auth($accountneeded) ) {
        print "Content-type: text/html\n\n";
    	print "<HTML><HEAD><TITLE>Login Required</TITLE></HEAD><BODY>Please login as admin first, before using this webpage</BODY></HTML>\n";
    	die;
}

&populateQueryFields;
$langfile=$path ."/lang/" .$queryString{"lang"};

if (open(IN,$langfile)) {
	while(<IN>) {
		if (!(/^#/)) {
			($key,$value)=/"([^"]+)"\s*,\s*"([^"]+)"/;
			$translation{$key}=$value;
		}
	}
	close(IN);
}


if ($cmd) {
    &populatePostFields if ($ENV{'REQUEST_METHOD'} eq 'POST');
    if ($queryString{"action"} eq "delData") {
        # GET : action=delData
        # POST :
        #    name=TEST
        #    path=/volume1/test
        print "Content-type: text/html\n\n";
        print del_path($postFields{"path"},$postFields{"name"},$postFields{"id"});
    } elsif ($queryString{"action"} eq "createPath") {
        # GET : action=createPath
        # POST :
        #    name=TEST
        #    path=/volume1/test
        print "Content-type: text/html\n\n";
        if (issecurestring($postFields{"name"}) && issecurepath($postFields{"path"})) {
            print add_new_path($postFields{"path"},$postFields{"name"});
        } else {
            print "{'success': false, 'errorInfo': '".trans('You have enter an illegal character.Only letters, numbers, underscores, spaces, points and slash are allowed')."!'}";
        }
    } elsif ($queryString{"action"} eq "listResult") {
        # do list result
        print "Content-type: text/html\n\n";
        print listResult($queryString{"callback"},$shares_conf,$queryString{"limit"},$queryString{"start"});
    } elsif ($queryString{"action"} eq "reloadapache") {
        sleep 2;
        print "Content-type: text/html\n\n";
        print reload_http()."\n";  
    } elsif ($queryString{"action"} eq "dobackupfiles") {
        print "Content-type: text/html\n\n";
        print dobackupfiles();        
    }
} else {
    print "Content-type: text/html\n\n";
    print trans("No valide parameter")."\n";
}


# translation subroutine
sub trans {
	local ($tt,$ret);
	$tt=@_[0];
	if ($translation{$tt}) {
		$ret=$translation{$tt};
	} else {
		$ret=$tt;
	}
	return $ret;
}
sub issecurestring
{
    my ($string) = @_;
    my $result = 1;
    # test if $val is secure for input text
    unless ( $string =~ m#^[\w \.-/]*$# ) {
        $result = 0;
    }
	return $result;   
}

sub issecurepath
{
    my ($path) = @_;
    my $result = 1;
    # test if $val is secure for input text
    unless ( $path =~ m#^/[\w \.-/]*$# ) {
        $result = 0;
    }
	return $result;   
}

sub is_admin_auth
{
    my ($account) = @_;
    
    my $user;
    my $result=0;
    
    if (open (IN,"/usr/syno/synoman/webman/modules/authenticate.cgi|")) {
    	$user=<IN>;
    	chop($user);
    	close(IN);
        # if not admin or no user at all...no authentication...so, bye-bye
        if ($user eq $account) {
            $result=1;
        }
    }
    return $result;
}

sub alreadyexist
{
    my ($name) = @_;
    my $result = 0;
    # Get shares out of shares.conf
	open(IN,"$shares_conf") or return "{'success': false, 'errorInfo': '".trans("Unable to open file")." ".$shares_conf.".'}";
	while(<IN>) {
	    # addShareFolder(1, "video", "/volume1/video");
		if (/^addShareFolder\(\d+,\s*\"$name\",\s*\".+\"\)/) {
            $result = 1;
		}
	}
	return $result;
}

sub reload_http
{
    my $efile="/tmp/ctrl.$$.error";
    my $ofile="/tmp/ctrl.$$.output";
    system("/usr/syno/etc/rc.d/S97apache-user.sh restart 2>$efile >$ofile");
    sleep(2); #extra sleep, to wait for processes to start or stop
	my $cmd_error="";
	my $cmd_log="";
    if (open(IN,$efile)) {
    	while(<IN>) { $cmd_error.=$_ };
    	close(IN);
    	unlink($efile);
    	}
    if (open(IN,$ofile)) {
    	while(<IN>) { $cmd_log.=$_ };
    	close(IN);
    	unlink($ofile);
    	}
    if ($cmd_log) {
    	$cmd_log=~s/([{}"[\]\[\(\)\;\'])/\\$1/g;
    	$cmd_log=~s#\n#<BR>#g;
    	}
    if ($cmd_error) {
    	$cmd_error=~s/([{}"[\]\[\(\)\;\'])/\\$1/g;
    	$cmd_error=~s#\n#<BR>#g;
    	}
    if ($cmd_log) {
    	return trans("Reload successful")." :".$cmd_log;
    }
    elsif ($cmd_error) {
    	return trans("Reload Error")." :".$cmd_error;
    }
}


sub listResult
{
    my ($callback,$sharesconf,$limit,$start) = @_;
    my @name =();
    my %folder = ();
    my %num = ();
    
    # Get shares out of shares.conf
	open(IN,"$sharesconf");
	while(<IN>) {
	    chomp();
		if (/^addShareFolder/) {
			my ($num,$nom,$path)=/^addShareFolder\((\d+),\s*\"([^\"]+)\",\s*\"([^\"]+)\"/;
			push(@name,"$nom");
			$folder{$nom}="$path";
			$num{$nom}="$num";
		}
	}
	close(IN);

	# generate the output Json of the data to be displayed
    my $out="";
    my $list="";
    my $i=0;
    foreach $n (@name) {
        if (($i >= $start) && ($i < ($start + $limit))) {
    	    my $path=$folder{"$n"};
    	    my $num=$num{"$n"};
    	    if ($list) {$list.=",\n" };
    	    $list.='{"id":"'.$num.'","name": "'.$n.'","path": "'.$path.'"}';
    	} 
    	$i++;
    }
    $out.="\n".$callback.'({'."\n";
    $out.='"totalCount":"'.$i.'",'."\n".'"results":['."\n";
    $out.=$list;
    $out.="\n".']})'."\n";

    return $out;
}

sub populateQueryFields {
    
    my $tmpStr = $ENV{ "QUERY_STRING" };
    @parts = split( /\&/, $tmpStr );
    foreach $part (@parts) {
        ( $name, $value ) = split( /\=/, $part );
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
        $value =~ ( s/%23/\#/g );
        $value =~ ( s/%2F/\//g );
        $value =~ s/<!--(.|\n)*-->//g;        
        $queryString{ "$name" } = $value;
    }
}

sub populatePostFields {

    read( STDIN, $tmpStr, $ENV{ "CONTENT_LENGTH" } );
    @parts = split( /\&/, $tmpStr );
    foreach $part (@parts) {
        ( $name, $value ) = split( /\=/, $part );
        $value =~ tr/+/ /;
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
        $value =~ ( s/%23/\#/g );
        $value =~ ( s/%2F/\//g );
        $value =~ s/<!--(.|\n)*-->//g;
        $postFields{ "$name" } = $value;
    }
}

sub dobackupfiles
{
    # do backup of files :  
    copy($shares_conf, ${fullpath}."backup/shares.conf") or return trans("The file")." '".$shares_conf."' ".trans("cannot be copied").".";
    copy($httpd_synoneuf_conf, ${fullpath}."backup/httpd-synoneuf.conf-user") or return trans("The file")." '".$httpd_synoneuf_conf."' ".trans("cannot be copied").".";
    return trans("Backup completed");
}



sub add_new_path
{
    my ($newpath,$newname) = @_;
    
    # be sur that the new path has no / at the end
    $newpath =~ s@(.*)/$@$1@;
 
     # verify that new name does not exist already
    if (alreadyexist($newname)) {
        return "{'success': false, 'errorInfo': '".trans("The new name")." ".$newname." ".trans("does already exist, please choose an other one")." !'}";
    }
   
    # verify that new path exist really
    if (! -d "$newpath") {
        return "{'success': false, 'errorInfo': '".trans("The new path")." ".$newpath." ".trans("does not exist on the synology host")."'}";
    }
    
    #####################################
    # shares.conf file
    #####################################
    open(IN,"$shares_conf") or return "{'success': false, 'errorInfo': '".trans("Unable to open file")." ".$shares_conf.".'}"; 
    my @shareconf = <IN>;
    close(IN);
    # overwrite the shares.conf file
    $ID = 1;
    if (open(OUT,">/tmp/$$.shares.conf")) { 
    	foreach my $line (@shareconf) {
    	    chomp($line);
    	    if ($line =~ /^\?>$/) {
    	        # end of shares.conf file
    	        print OUT 'addShareFolder('.$ID.', "'.$newname.'", "'.$newpath.'");'."\n";
    	        print OUT '?>'."\n";
    	    } else {
    	        $ID++ if ($line =~ /^addShareFolder/);
    	        print OUT $line."\n";
    	    }
    	}
    	close(OUT);
    } else {
        return "{'success': false, 'errorInfo': '".trans("Unable to write file")." /tmp/$$.shares.conf.'}";
    }

    #####################################
    # httpd-synoneuf.conf-user file
    #####################################
    
    open(IN,"$httpd_synoneuf_conf")  or return "{'success': false, 'errorInfo': '".trans("Unable to open file")." ".$httpd_synoneuf_conf.".'}"; 
    my @httpdsynoneuf = <IN>;
    close(IN);
    # overwrite the httpd-synoneuf.conf-user file
    $ID = 1;
    if (open(OUT,">/tmp/$$.httpd-synoneuf.conf-user")) { 
    	foreach my $line (@httpdsynoneuf) {
    	    chomp($line);
    	    if ($line =~ m#^</VirtualHost>$#) {
    	        # end of httpd-synoneuf.conf-user file
    	        print OUT '  Alias /__mp9ctl_share_'.$ID.'/ "'.$newpath.'/"'."\n";
    	        print OUT '</VirtualHost>'."\n";
    	    } else {
    	        if (my ($openbasedir) = ($line =~ m#^\s*php_admin_value open_basedir "([^"]+)"#)) {
    	            # ajout du nouveau chemin dans l open_basedir du vhosts
    	            print OUT '  php_admin_value open_basedir "'.$openbasedir.':'.$newpath.'"'."\n";
    	        }  else {
    	            $ID++ if ($line =~ m#Alias /__mp9ctl_share_#);
    	            print OUT $line."\n";
    	        }
    	    }
    	}
    	close(OUT);
    } else {
        return "{'success': false, 'errorInfo': '".trans("Unable to write file")." /tmp/$$.httpd-synoneuf.conf-user.'}";
    }
    
    #####################################
    # apply changes on all files
    #####################################
    move("/tmp/$$.shares.conf",$shares_conf) or return "{'success': false, 'errorInfo': '".trans("Unable to move file")." /tmp/$$.shares.conf ".trans("to")." $shares_conf.'}";
    move("/tmp/$$.httpd-synoneuf.conf-user",$httpd_synoneuf_conf) or return "{'success': false, 'errorInfo': '".trans("Unable to move file")." /tmp/$$.httpd-synoneuf.conf-user ".trans("to")." $httpd_synoneuf_conf.'}";

    return "{'success': true, 'info': '".trans("The new path")." ".$newname." (".$newpath.") ".trans("is added").", ".trans("don t forget to reload apache")." !'}";
}

sub del_path
{
    my ($delpath,$delname,$delid) = @_;
    
    #####################################
    # shares.conf file
    #####################################
    
    open(IN,"$shares_conf") or return "{'success': false, 'errorInfo': '".trans("Unable to open file")." ".$shares_conf.".'}"; 
    my @shareconf = <IN>;
    close(IN);
    # overwrite the shares.conf file
    $ID = 1;
    if (open(OUT,">/tmp/$$.shares.conf")) { 
    	foreach my $line (@shareconf) {
    	    chomp($line);
    	    if (my ($num,$name,$path) = ($line =~ m@addShareFolder\((\d+), "(\w+)", "([^"]+)"\);@)) {
    	        # it's a addShareFolder line
    	        if (($name eq $delname) && ($path eq $delpath) && ($num eq $delid)) {
    	            # it's THE delpath and delname, so do not write this line
    	        } else {
    	            print OUT 'addShareFolder('.$ID.', "'.$name.'", "'.$path.'");'."\n";
    	            $ID++;
    	        }
    	    } else {
    	        print OUT $line."\n";
    	    }
    	}
    	close(OUT);
    } else {
        return "{'success': false, 'errorInfo': '".trans("Unable to write file")." /tmp/$$.shares.conf.'}";
    }

    
    #####################################
    # httpd-synoneuf.conf-user file
    #####################################
    
    open(IN,"$httpd_synoneuf_conf")  or return "{'success': false, 'errorInfo': '".trans("Unable to open file")." ".$httpd_synoneuf_conf.".'}"; 
    my @httpdsynoneuf = <IN>;
    close(IN);
    # overwrite the httpd-synoneuf.conf-user file
    $ID = 1;
    if (open(OUT,">/tmp/$$.httpd-synoneuf.conf-user")) { 
    	foreach my $line (@httpdsynoneuf) {
    	    chomp($line);
    	    if (my ($num,$path) = ($line =~ m@\s*Alias /__mp9ctl_share_(\d+)/ "([^"]+)"@)) {
    	        # it's a addShareFolder line
    	        if (($path eq $delpath."/") && ($num eq $delid)) {
    	            # it's THE delpath and delname, so do not write this line
    	        } else {
   	                print OUT '  Alias /__mp9ctl_share_'.$ID.'/ "'.$path.'"'."\n";
   	                $ID++;
    	        }
    	    } else {
   	            if (my ($openbasedir) = ($line =~ m#^\s*php_admin_value open_basedir "([^"]+)"#)) {
                   	if ($openbasedir =~ /:$delpath/) {
                   		$openbasedir=~ s@:$delpath@@;
                   	} else {
                   		$openbasedir=~ s@$delpath:@@;
                   	}
                   	print OUT '  php_admin_value open_basedir "'.$openbasedir.'"'."\n";
                } else {
    	            print OUT $line."\n";
    	        }
    	    }
    	}
    	close(OUT);
    } else {
        return "{'success': false, 'errorInfo': '".trans("Unable to write file")." /tmp/$$.httpd-synoneuf.conf-user.'}";
    }
    
    #####################################
    # apply changes on all files
    #####################################
    move("/tmp/$$.shares.conf",$shares_conf) or return "{'success': false, 'errorInfo': '".trans("Unable to move file")." /tmp/$$.shares.conf ".trans("to")." $shares_conf.'}";
    move("/tmp/$$.httpd-synoneuf.conf-user",$httpd_synoneuf_conf) or return "{'success': false, 'errorInfo': '".trans("Unable to move file")." /tmp/$$.httpd-synoneuf.conf-user ".trans("to")." $httpd_synoneuf_conf.'}";   
    return "{'success': true, 'info': '".trans("Path")." ".$delname." (".$delpath.") ".trans("deleted").", ".trans("don t forget to reload apache")." !'}";
}